/*
 * Copyright (c) 2009, 2012 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Dave Locke - initial API and implementation and/or initial documentation
 */
package org.eclipse.paho.client.mqttv3.internal;

/**
 * FileLock - used to obtain a lock that can be used to prevent other MQTT clients using the same persistent store. If the lock is already held then an exception is thrown.
 *
 * Some Java runtimes such as JME MIDP do not support file locking or even the Java classes that support locking. The class is coded to both compile and work on all Java runtimes.
 * In Java runtimes that do not support locking it will look as though a lock has been obtained but in reality no lock has been obtained.
 */
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;

public class FileLock {

    private File lockFile;
    private RandomAccessFile file;
    private Object fileLock;

    /**
     * Creates an NIO FileLock on the specified file if on a suitable Java runtime.
     *
     * @param clientDir the a File of the directory to contain the lock file.
     * @param lockFilename name of the the file to lock
     * @throws Exception if the lock could not be obtained for any reason
     */
    public FileLock(File clientDir, String lockFilename) throws Exception {
        // Create a file to obtain a lock on.
        lockFile = new File(clientDir, lockFilename);
        if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) {
            try {
                this.file = new RandomAccessFile(lockFile, "rw");
                Method m = file.getClass().getMethod("getChannel", new Class[]{});
                Object channel = m.invoke(file, new Object[]{});
                m = channel.getClass().getMethod("tryLock", new Class[]{});
                this.fileLock = m.invoke(channel, new Object[]{});
            } catch (NoSuchMethodException nsme) {
                this.fileLock = null;
            } catch (IllegalArgumentException iae) {
                this.fileLock = null;
            } catch (IllegalAccessException iae) {
                this.fileLock = null;
            }
            if (fileLock == null) {
                // Lock not obtained
                release();
                throw new Exception("Problem obtaining file lock");
            }
        }
    }

    /**
     * Releases the lock.
     */
    public void release() {
        try {
            if (fileLock != null) {
                Method m = fileLock.getClass().getMethod("release", new Class[]{});
                m.invoke(fileLock, new Object[]{});
                fileLock = null;
            }
        } catch (Exception e) {
            // Ignore exceptions
        }
        if (file != null) {
            try {
                file.close();
            } catch (IOException e) {
            }
            file = null;
        }

        if (lockFile != null && lockFile.exists()) {
            lockFile.delete();
        }
        lockFile = null;
    }

}
